package com.crm.common.result;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.json.JsonMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.geo.GeoModule;
import org.springframework.lang.Nullable;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Objects;

/**
 * 工具类：JSON转换/序列化工具类
 *
 * @author richie696
 * @version 1.3
 * @since 2017-9-22
 */
@Slf4j
@SuppressWarnings("unused")
class JsonUtils {

	private final JsonMapper MAPPER_LOWER_CAMEL_CASE;
	private final JsonMapper MAPPER_SNAKE_CASE;

	private static class Bean {
		static final JsonUtils INSTANCE = new JsonUtils();
	}

	public static JsonUtils getInstance() {
		return Bean.INSTANCE;
	}

	private JsonUtils() {
		MAPPER_LOWER_CAMEL_CASE = JsonMapper.builder()
				.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
				.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
				.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
				.serializationInclusion(JsonInclude.Include.NON_NULL)
				.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
				.defaultDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
				.addModule(new GeoModule())
				.build();
		MAPPER_SNAKE_CASE = JsonMapper.builder()
				.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true)
				.configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true)
				.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
				.serializationInclusion(JsonInclude.Include.NON_NULL)
				.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
				.propertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE)
				.defaultDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"))
				.addModule(new GeoModule())
				.build();
	}

	/**
	 * 序列化对象为JSON字符串的方法（会过滤掉null值）
	 *
	 * @param value         待序列化的对象
	 * @param prettyPrinter 输出美化后的JSON字符串
	 * @return 返回JSON字符串（如果对象转换错误将返回""字符串）
	 */
	@Nullable
	public String serialize(Object value, boolean... prettyPrinter) {
		try {
			if (prettyPrinter != null && prettyPrinter.length > 0 && prettyPrinter[0]) {
				return MAPPER_LOWER_CAMEL_CASE.writerWithDefaultPrettyPrinter().writeValueAsString(value);
			}
			return MAPPER_LOWER_CAMEL_CASE.writeValueAsString(value);
		} catch (JsonProcessingException e) {
			log.error("序列化失败。(content={}, stack={})", value, e.getMessage());
			return null;
		}
	}

	/**
	 * 序列化对象为JSON字符串的方法
	 *
	 * @param value         待序列化的对象
	 * @param prettyPrinter 输出美化后的JSON字符串
	 * @return 返回JSON字符串（如果对象转换错误将返回""字符串）
	 */
	@Nullable
	public String serializeBySnake(Object value, boolean... prettyPrinter) {
		try {
			if (prettyPrinter != null && prettyPrinter.length > 0 && prettyPrinter[0]) {
				return MAPPER_SNAKE_CASE.writerWithDefaultPrettyPrinter().writeValueAsString(value);
			}
			return MAPPER_SNAKE_CASE.writeValueAsString(value);
		} catch (JsonProcessingException e) {
			log.error("序列化失败。(content={}, stack={})", value, e.getMessage());
			return null;
		}
	}

	/**
	 * 将指定对象序列化为字节数组的方法
	 *
	 * @param value 需要序列化的对象
	 * @return 返回字节数组
	 */
	@Nullable
	public byte[] serializeBytes(Object value) {
		try {
			return MAPPER_LOWER_CAMEL_CASE.writeValueAsBytes(value);
		} catch (JsonProcessingException e) {
			log.error("序列化失败。(content={}, stack={})", value, e.getMessage());
			return null;
		}
	}

	/**
	 * 将指定对象序列化为字节数组的方法
	 *
	 * @param value 需要序列化的对象
	 * @return 返回字节数组
	 */
	@Nullable
	public byte[] serializeBytesBySnake(Object value) {
		try {
			return MAPPER_SNAKE_CASE.writeValueAsBytes(value);
		} catch (JsonProcessingException e) {
			log.error("序列化失败。(content={}, stack={})", value, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON字符串的方法
	 *
	 * @param content   JSON字符串
	 * @param valueType 反序列化字符串的类型
	 * @param <T>       JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserialize(String content, Class<T> valueType) {
		try {
			return MAPPER_LOWER_CAMEL_CASE.readValue(content, valueType);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", content, valueType, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON字符串的方法
	 *
	 * @param content   JSON字符串
	 * @param valueType 反序列化字符串的类型
	 * @param <T>       JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserializeBySnake(String content, Class<T> valueType) {
		try {
			return MAPPER_SNAKE_CASE.readValue(content, valueType);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", content, valueType, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON字符串的方法
	 *
	 * @param content      JSON字符串
	 * @param valueTypeRef 反序列化字符串的类型
	 * @param <T>          JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserialize(String content, TypeReference<T> valueTypeRef) {
		try {
			return MAPPER_LOWER_CAMEL_CASE.readValue(content, valueTypeRef);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", content, valueTypeRef, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON字符串的方法
	 *
	 * @param content      JSON字符串
	 * @param valueTypeRef 反序列化字符串的类型
	 * @param <T>          JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserializeBySnake(String content, TypeReference<T> valueTypeRef) {
		try {
			return MAPPER_SNAKE_CASE.readValue(content, valueTypeRef);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", content, valueTypeRef, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON Payload的方法
	 *
	 * @param payload   数据负载
	 * @param valueType 反序列化字符串的类型
	 * @param <T>       JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserializePayload(byte[] payload, Class<T> valueType) {
		try {
			return MAPPER_LOWER_CAMEL_CASE.readValue(payload, valueType);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={}, class={}, stack={})",
					new String(payload), valueType, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON Payload的方法
	 *
	 * @param payload   数据负载
	 * @param valueType 反序列化字符串的类型
	 * @param <T>       JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserializePayloadBySnake(byte[] payload, Class<T> valueType) {
		try {
			return MAPPER_SNAKE_CASE.readValue(payload, valueType);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={}, class={}, stack={})",
					new String(payload), valueType, e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON Payload的方法
	 *
	 * @param payload      数据负载
	 * @param valueTypeRef 反序列化字符串的类型
	 * @param <T>          JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserializePayload(byte[] payload, TypeReference<T> valueTypeRef) {
		try {
			return MAPPER_LOWER_CAMEL_CASE.readValue(payload, valueTypeRef);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={}, stack={})",
					new String(payload), e.getMessage());
			return null;
		}
	}

	/**
	 * 反序列化JSON Payload的方法
	 *
	 * @param payload      数据负载
	 * @param valueTypeRef 反序列化字符串的类型
	 * @param <T>          JSON字符串对应的实体类型
	 * @return 返回反序列化后的结果
	 */
	@Nullable
	public <T> T deserializePayloadBySnake(byte[] payload, TypeReference<T> valueTypeRef) {
		try {
			return MAPPER_SNAKE_CASE.readValue(payload, valueTypeRef);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={}, stack={})",
					new String(payload), e.getMessage());
			return null;
		}
	}

	@Nullable
	public <T> T convertMapToBean(Object obj, TypeReference<T> valueTypeRef) {
		try {
			String serialize = serialize(obj);
			return MAPPER_LOWER_CAMEL_CASE.readValue(serialize, valueTypeRef);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", obj, valueTypeRef, e.getMessage());
			return null;
		}
	}

	@Nullable
	public <T> T convertMapToBeanBySnake(Object obj, TypeReference<T> valueTypeRef) {
		try {
			String serialize = serialize(obj);
			return MAPPER_SNAKE_CASE.readValue(serialize, valueTypeRef);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", obj, valueTypeRef, e.getMessage());
			return null;
		}
	}

	@Nullable
	public <T> T convertMapToBean(Object obj, Class<T> valueType) {
		try {
			String serialize = serialize(obj);
			return MAPPER_LOWER_CAMEL_CASE.readValue(serialize, valueType);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", obj, valueType, e.getMessage());
			return null;
		}
	}

	@Nullable
	public <T> T convertMapToBeanBySnake(Object obj, Class<T> valueType) {
		try {
			String serialize = serialize(obj);
			return MAPPER_SNAKE_CASE.readValue(serialize, valueType);
		} catch (IOException e) {
			log.error("当前内容与需要转换的类型不匹配，转换失败。(content={},class={}, stack={})", obj, valueType, e.getMessage());
			return null;
		}
	}

	/**
	 * 将对象转换为通用JSON格式对象的方法
	 *
	 * @param obj 待转换的对象
	 * @return 返回 JSON 通用格式对象
	 */
	@Nullable
	public JsonNode toJsonNode(Object obj) {
		if (Objects.isNull(obj)) {
			throw new IllegalArgumentException("待解析的对象不能为null。");
		}
		String serialize = serialize(obj);
		try {
			return MAPPER_LOWER_CAMEL_CASE.readTree(serialize);
		} catch (JsonProcessingException e) {
			log.error("当前内容无法转换为JsonNode对象，转换失败。", e);
			return null;
		}
	}

	/**
	 * 将对象转换为通用JSON格式对象的方法
	 *
	 * @param obj 待转换的对象
	 * @return 返回 JSON 通用格式对象
	 */
	@Nullable
	public JsonNode toJsonNodeBySnake(Object obj) {
		if (Objects.isNull(obj)) {
			throw new IllegalArgumentException("待解析的对象不能为null。");
		}
		String serialize = serialize(obj);
		try {
			return MAPPER_SNAKE_CASE.readTree(serialize);
		} catch (JsonProcessingException e) {
			log.error("当前内容无法转换为JsonNode对象，转换失败。", e);
			return null;
		}
	}

}
